/**
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 * 
 *         http://www.apache.org/licenses/LICENSE-2.0
 *         
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. Unless required by applicable law or
 * agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 */
package org.rhok.pdx.ncn.storage;

import java.util.ArrayList;
import java.util.List;

import org.rhok.pdx.ncn.application.ApplicationContext;
import org.rhok.pdx.ncn.datatypes.LocationData;

import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

/**
 * Database manager classed, used to interact with the SQLite database. Contains convenience
 * methods to avoid having to deal directly with the database cursor.
 * @author pfritzsche
 */
public class NCNDatabaseManager {
	/**
	 * Context interface specific to this class. Only allows access to the methods the class needs
	 * access to, in order to prevent accidental changes to shared data. Documentation provided at
	 * {@link ApplicationContext}.
	 * @author pfritzsche
	 */
	public interface NCNDatabaseManagerContext {
		public Activity app();
	}
	
	private final int DB_VERSION = 1;
	private final String
		DB_NAME = "ncn",
		TABLE_NAME = "location_data",
		COL_ID = "id",
		COL_1_TIME = "timestamp",
		COL_2_LAT = "latitude",
		COL_3_LON = "longitude",
		COL_4_SIG = "signal_strength",
		COL_5_WIFI = "wifi_strength";
	
	private final MySQLiteOpenHelper helper;
	private SQLiteDatabase db;
	
	/**
	 * Public constructor.
	 * @param context Shared application context, to be used for data sharing between classes
	 */
	public NCNDatabaseManager(NCNDatabaseManagerContext context) {
		helper = new MySQLiteOpenHelper(context.app());
	}
	
	public void open() {
		db = helper.getWritableDatabase();
	}
	
	public void close() {
		db.close();
	}
	
	/**
	 * Adds a new row to the database.
	 * @param data {@link LocationData} object containing information to be added to the database
	 * @return id of the newly inserted row
	 */
	public long addRow(LocationData data) {
		ContentValues values = new ContentValues();
		values.put(COL_1_TIME, data.timestamp());
		values.put(COL_2_LAT, data.lat());
		values.put(COL_3_LON, data.lng());
		values.put(COL_4_SIG, data.signal());
		values.put(COL_5_WIFI, data.wifi());
		return db.insert(TABLE_NAME, null, values);
	}
	
	/**
	 * Adds a list of rows to the database.
	 * @param data List<LocationData> object containing information to be added to the database
	 * @return id of the newly inserted row
	 */
	public void addRows(List<LocationData> data) {
		for (LocationData d : data) {
			addRow(d);
		}
	}
	
	/**
	 * Deletes a specified row from the database
	 * @param rowId id of the row to be deleted
	 */
	public void deleteRow(long rowId) {
		db.delete(TABLE_NAME, COL_ID + "=" + rowId, null);
	}
	
	/**
	 * Returns all rows, starting with ones containing data gathered after the specified time
	 * @param startTime the earliest time to gather data for, in millis since epoch
	 * @return List<LocationData> containing information from all returned rows 
	 */
	public List<LocationData> getRows(long startTime) {
		ArrayList<LocationData> data = new ArrayList<LocationData>();
		
		Cursor cursor = db.query(TABLE_NAME, null, COL_1_TIME + " > " + startTime, null, null, null,
				COL_1_TIME + " DESC");
		cursor.moveToFirst();
		if (!cursor.isAfterLast()) {
			do {
				data.add(new LocationData(
						cursor.getLong(1), cursor.getDouble(2), cursor.getDouble(3),
						cursor.getInt(4), cursor.getInt(5)));
			} while (cursor.moveToNext());
		}
		
		cursor.close();
		return data;
	}
	
	/**
	 * Returns the most recent item inserted into the database
	 * @return LocationData representing the most recent row
	 */
	public LocationData getMostRecentLocationData() {
		Cursor cursor = db.query(TABLE_NAME, null,
				COL_1_TIME + " > " + (System.currentTimeMillis() - 86400000), null, null, null,
				COL_1_TIME + " DESC");
		LocationData data = null;
		cursor.moveToFirst();
		
		if (!cursor.isAfterLast()) {
			data = new LocationData(cursor.getLong(0), cursor.getDouble(1),
					cursor.getDouble(2), cursor.getInt(3), cursor.getInt(4));
		}
		cursor.close();
		return data;
	}
	
	/**
	 * Helper class, only used to create the database on first application run.
	 * @author pfritzsche
	 */
	private class MySQLiteOpenHelper extends SQLiteOpenHelper {
		/**
		 * Public constructor.
		 * @param context Android system application context
		 */
		public MySQLiteOpenHelper(Context context) {
			super(context, DB_NAME, null, DB_VERSION);
		}

		/**
		 * Called when the database is first created. Initializes the location data table.
		 */
		@Override public void onCreate(SQLiteDatabase db) {
			StringBuilder sb = new StringBuilder();
			sb.append("create table ").append(TABLE_NAME).append(" (");
			sb.append(COL_ID).append(" integer primary key autoincrement not null, ");
			sb.append(COL_1_TIME).append(" integer, ");
			sb.append(COL_2_LAT).append(" real, ");
			sb.append(COL_3_LON).append(" real, ");
			sb.append(COL_4_SIG).append(" integer, ");
			sb.append(COL_5_WIFI).append(" integer);");
			db.execSQL(sb.toString());
		}

		/**
		 * Required method declaration. We don't care about upgrading, as this is v1, so we leave
		 * this function blank. 
		 * 
		 * Note: http://bit.ly/szZnh8
		 */
		@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { }
	}
}
